package com.avcompris.util.junit;

import static com.avcompris.util.ExceptionUtils.nonNullArgument;
import static org.apache.commons.io.FileUtils.openInputStream;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Properties;

import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import com.avcompris.common.annotation.Nullable;
import com.avcompris.util.AbstractUtils;
import com.avcompris.util.XsltResourceURIResolver;

/**
 * utilities for test classes that operate on YAML files.
 * 
 * @author David Andriana Copyright Avantage Compris SARL 2008-2009 ©
 */

public abstract class JvyamlTestUtils extends AbstractUtils {

	/**
	 * return a String created with the content of an XML File.
	 * 
	 * @param s the original XML String
	 * @return the resulting XML String, without whitespaces
	 */
	public static String eraseWhitespaces(
			final String s) throws Exception {

		nonNullArgument(s, "s");

		final StringBuilder sb = new StringBuilder();
		boolean afterGt = false;
		boolean metWhitespace = false;

		for (final char c : s.toCharArray()) {

			if (afterGt) {
				if (Character.isWhitespace(c)) {
					metWhitespace = true;
					continue;
				} else if (c == '<') {
					afterGt = false;
				} else {
					if (metWhitespace) {
						throw new RuntimeException("Illegal char [" + c
								+ "] between [>...<]");
					} else {
						afterGt = false;
						metWhitespace = false;
					}
				}
			} else if (c == '>') {
				afterGt = true;
				metWhitespace = false;
			}

			sb.append(c);
		}

		return sb.toString();
	}

	/**
	 * transform an XML via an XSLT Stylesheet.
	 * 
	 * @param xml the XML content to transform
	 * @param xsltFilename the XSLT File's name
	 * @param result the XSLT result
	 */
	private static void transform(
			final String xml,
			final String xsltFilename,
			final Result result) throws Exception {

		nonNullArgument(xml, "xml");
		nonNullArgument(xsltFilename, "xsltFilename");
		nonNullArgument(result, "result");

		final TransformerFactory tFactory = TransformerFactory.newInstance();

		tFactory.setURIResolver(new XsltResourceURIResolver(
				JvyamlTestUtils.class));

		final Transformer t = tFactory.newTransformer(new StreamSource(
				new File(xsltFilename)));

		t.transform(new StreamSource(new StringReader(xml)), result);
	}

	/**
	 * transform an XML via an XSLT Stylesheet.
	 * 
	 * @param xml the XML content to transform
	 * @param xsltFilename the XSLT File's name
	 * @return the transformation result
	 */
	public static String transform(
			final String xml,
			final String xsltFilename) throws Exception {

		nonNullArgument(xml, "xml");
		nonNullArgument(xsltFilename, "xsltFilename");

		final Writer writer = new StringWriter();

		transform(xml, xsltFilename, new StreamResult(writer));

		return writer.toString();
	}

	/**
	 * transform an XML via an XSLT Stylesheet.
	 * 
	 * @param xml the XML content to transform
	 * @param xsltFilename the XSLT File's name
	 * @param params the params for the XSLT
	 * @return the transformation result
	 */
	public static String transform(
			final String xml,
			final String xsltFilename,
			@Nullable final Properties params) throws Exception {

		nonNullArgument(xml, "xml");
		nonNullArgument(xsltFilename, "xsltFilename");

		final Writer writer = new StringWriter();

		transform(xml, xsltFilename, params, new StreamResult(writer));

		return writer.toString();
	}

	/**
	 * transform an XML via an XSLT Stylesheet.
	 * 
	 * @param xml the XML content to transform
	 * @param xsltFilename the XSLT File's name
	 * @param result the XSLT result
	 * @param params the params for the XSLT
	 */
	private static void transform(
			final String xml,
			final String xsltFilename,
			@Nullable final Properties params,
			final Result result) throws Exception {

		nonNullArgument(xml, "xml");
		nonNullArgument(xsltFilename, "xsltFilename");
		nonNullArgument(result, "result");

		final TransformerFactory tFactory = TransformerFactory.newInstance();

		tFactory.setURIResolver(new XsltResourceURIResolver(
				JvyamlTestUtils.class));

		final InputStream is;

		final File file = new File(xsltFilename);

		if (file.exists()) {

			is = openInputStream(file);

		} else {

			if (!xsltFilename.startsWith("/")) {
				throw new RuntimeException(
						"XSLT path should start with \"/\": [" + xsltFilename
								+ "]");
			}

			final ClassLoader classLoader;

			classLoader = Thread.currentThread().getContextClassLoader();

			is = classLoader.getResourceAsStream(xsltFilename.substring(1));

			if (is == null) {
				throw new IOException("Cannot load XSLT resource: ["
						+ xsltFilename + "]");
			}
		}

		try {

			final Transformer t = tFactory.newTransformer(new StreamSource(is));

			if (params != null) {
				for (final Enumeration<?> e = params.propertyNames(); e
						.hasMoreElements();) {
					final String name = (String) e.nextElement();
					final String value = params.getProperty(name);
					t.setParameter(name, value);
				}
			}

			t.transform(new StreamSource(new StringReader(xml)), result);

		} finally {

			is.close();
		}
	}
}
